# Copyright (c) [2022] Huawei Technologies Co.,Ltd.ALL rights reserved.
# This program is licensed under Mulan PSL v2.
# You can use it according to the terms and conditions of the Mulan PSL v2.
#          http://license.coscl.org.cn/MulanPSL2
# THIS PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
####################################
# @Author  :
# @email   :
# @Date    :
# @License : Mulan PSL v2
#####################################

import json
import datetime
import requests
import pytz

from flask import g, current_app

from celeryservice import celeryconfig
from server.model import User
from server.utils.response_util import RET
from server import db
from server.model.vmachine import Vmachine
from server.model.pmachine import Pmachine
from celeryservice.lib import TaskHandlerBase
from server.utils.response_util import ssl_cert_verify_error_collect
from server.model.message import Message, MsgType, MsgLevel
from server.utils.db import Insert, Edit
from server.model.permission import Role, ReUserRole
from server.apps.pmachine.handlers import PmachineOccupyReleaseHandler


class LifecycleMonitor(TaskHandlerBase):
    def check_vmachine_lifecycle(self):
        v_machines = Vmachine.query.all()

        for vmachine in v_machines:
            end_time = vmachine.end_time

            if datetime.datetime.now(tz=pytz.timezone("Asia/Shanghai")) > \
                    end_time.astimezone(pytz.timezone('Asia/Shanghai')):
                self.logger.info(
                    "vmachine {} is going to be destroyed, with end_time {}".format(
                        vmachine.name, vmachine.end_time
                    )
                )
                db.session.delete(vmachine)

        db.session.commit()

    def check_pmachine_lifecycle(self):
        filter_params = [
            Pmachine.state == "occupied",
            Pmachine.end_time.isnot(None),
        ]
        pmachines = Pmachine.query.filter(*filter_params).all()
        pmachine_handler = PmachineOccupyReleaseHandler()
        for pmachine in pmachines:
            end_time = pmachine.end_time
            try:
                if datetime.datetime.now(tz=pytz.timezone("Asia/Shanghai")) > \
                        end_time.astimezone(pytz.timezone('Asia/Shanghai')):
                    self.logger.info(
                        "pmachine {} is going to be released, with end_time {}".format(
                            pmachine.bmc_ip, pmachine.end_time
                        )
                    )

                    body = dict()
                    body.update({
                        "ip": pmachine.ip,
                        "port": pmachine.port,
                        "user": pmachine.user,
                        "password": pmachine.password,
                        "bmc_ip": pmachine.bmc_ip,
                        "bmc_user": pmachine.bmc_user,
                        "bmc_password": pmachine.bmc_password
                    })
                    _resp = CeleryMonitorMessenger(body).send_request(
                        pmachine.machine_group, "/api/v1/pmachine/auto-release-check", method="post")
                    if _resp.get("error_code") != RET.OK:
                        # 避免重复发生通知
                        if pmachine.is_release_notification == 1:
                            continue
                        check_res = _resp.get("error_msg")
                        if pmachine.permission_type in ["org", "person"]:
                            role = Role.query.filter_by(name="admin", org_id=pmachine.org_id).first()
                        elif pmachine.permission_type == "group":
                            role = Role.query.filter_by(
                                name="admin", org_id=pmachine.org_id, group_id=pmachine.group_id
                            ).first()
                        else:
                            continue

                        re_role_user = ReUserRole.query.filter_by(role_id=role.id).all()
                        Message.create_instance(json.dumps({'info': check_res}),
                                                1, [item.user_id for item in re_role_user], pmachine.org_id,
                                                level=MsgLevel.system.value, msg_type=MsgType.text.value)
                        Message.create_instance(json.dumps({'info': check_res}),
                                                1, [pmachine.occupier], pmachine.org_id,
                                                level=MsgLevel.system.value, msg_type=MsgType.text.value)

                        Edit(Pmachine, {"id": pmachine.id, "is_release_notification": 1}).single(Pmachine, "/pmachine")
                    else:
                        # 构建释放请求上下文
                        with current_app.test_request_context():
                            occupier_user = User.query.filter_by(user_id=pmachine.occupier_id).first()
                            g.user_id = occupier_user.user_id
                            g.user_login = occupier_user.user_login
                            pmachine_handler.release_with_release_scopes(pmachine)
            except Exception as e:
                self.logger.info("pmachine release error:{}".format(e))
                continue

    def main(self):
        self.check_pmachine_lifecycle()
        self.check_vmachine_lifecycle()


class CeleryMonitorMessenger:
    def __init__(self, body):
        self._body = body

    @ssl_cert_verify_error_collect
    def send_request(self, machine_group, api, method="get"):
        request_url = "https://{}:{}{}".format(machine_group.messenger_ip, machine_group.messenger_listen, api)
        verify = celeryconfig.cacert_path
        headers = {"content-type": "application/json;charset=utf-8"}

        if method == "post":
            resp = requests.post(request_url, json=self._body, headers=headers, verify=verify)
        else:
            resp = requests.get(request_url, params=self._body, headers=headers, verify=verify)

        if resp.status_code != 200:
            return dict(
                error_code=RET.RUNTIME_ERROR,
                error_msg=f"could not reach messenger of this machine group, "
                          f"auto release pmanchine {self._body.get('ip')} failed!"
            )
        try:
            result = json.loads(resp.text)
        except AttributeError:
            result = resp.json()
        return result
